﻿using DotNetCommon.Extensions;
using DotNetCommon.Logger;
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;

namespace DotNetCommon;

/// <summary>
/// 表示机器信息
/// </summary>
public class Machine
{
    private static readonly ILogger logger = LoggerFactory.CreateLogger("DotNetCommon.Machine");
    public const string MachineIdKey = "MachineId";
    public const string MachineIdStringKey = "MachineIdString";
    public const string MachineTxtKey = "machine.txt";

    static Machine()
    {
        SetMachineFlagByDefault();
    }

    #region 分布式集群中的机器Id
    private static int _machineId = 0;
    private static string _machineIdString = string.Empty;
    /// <summary>
    /// 机器Id,>=0，默认0, 根据集群的大小合理设置机器Id<br/>
    /// 用在分布式Id生成中
    /// </summary>
    public static int MachineId => _machineId;

    /// <summary>
    /// 机器IdString,默认: null，根据集群的大小合理设置机器IdString<br/>
    /// 用在分布式流水号生成中，当值为null时，取 <see cref="Machine.MachineId"/>
    /// </summary>
    public static string MachineIdString => _machineIdString ?? _machineId.ToString();

    /// <summary>
    /// 设置机器Id和机器IdString
    /// </summary>
    /// <param name="MachineId">机器Id, >=0，默认0, 用在分布式Id生成中</param>
    /// <param name="MachineIdString">机器IdString, 默认: null，用在分布式流水号生成中，当值为null时，取 <see cref="Machine.MachineId"/></param>
    public static void SetMachineId(int MachineId, string MachineIdString)
    {
        if (MachineId < 0) throw new Exception("MachineId的值必须大于等于0!");
        _machineId = MachineId;
        _machineIdString = MachineIdString;
    }

    /// <summary>
    /// 分别从命令行、程序目录下(<c>"./machine.txt,../machine.txt 最多网上6层"</c>)、环境变量 读取设置值, 优先级为:
    /// <list type="bullet">
    /// <item>命令行, 示例: dotnet xxx.dll --MachineId 12 --MachineIdString ABCD</item>
    /// <item>machine.txt, 文件示例: 
    /// <code>
    /// MachineId=12
    /// MachineIdString=ABCD
    /// </code>
    /// </item>
    /// <item>环境变量, 不需要示例</item>
    /// </list>
    /// </summary>
    /// <remarks>注意: MachineId/MachineIdString 不区分大小写</remarks>
    public static void SetMachineFlagByDefault()
    {
        int machineId = 0;
        var hasReadMachineId = false;
        string machineIdString = null;
        var hasReadMachineIdString = false;

        #region 先从命令行中读取
        //dotnet xxx.dll --MachineId=12 --MachineIdString=ABCD
        //dotnet xxx.dll --MachineId 12 --MachineIdString ABCD
        //dotnet xxx.dll MachineId=12 MachineIdString=ABCD
        try
        {
            var args = Environment.GetCommandLineArgs();
            var idx1 = args.FindIndex(i => string.Equals(i?.TrimStart('-')?.Split('=').FirstOrDefault(), MachineIdKey, StringComparison.OrdinalIgnoreCase));
            if (idx1 >= 0)
            {
                var tmpIdx = args[idx1].IndexOf('=');
                if (tmpIdx > 0) hasReadMachineId = int.TryParse(args[idx1].AsSpan(tmpIdx + 1), out machineId);
                else if (idx1 < args.Length - 1) hasReadMachineId = int.TryParse(args[idx1 + 1], out machineId);
                if (machineId < 0) hasReadMachineId = false;
            }
            var idx2 = args.FindIndex(i => string.Equals(i?.TrimStart('-').Split('=').FirstOrDefault(), MachineIdStringKey, StringComparison.OrdinalIgnoreCase));
            if (idx2 >= 0)
            {
                var tmpIdx = args[idx2].IndexOf('=');
                if (tmpIdx > 0)
                {
                    machineIdString = args[idx2][(tmpIdx + 1)..].Trim();
                    hasReadMachineIdString = machineIdString.IsNotNullOrEmpty();
                }
                else if (idx2 < args.Length - 1)
                {
                    machineIdString = args[idx2 + 1].Trim();
                    hasReadMachineIdString = machineIdString.IsNotNullOrEmpty();
                }
            }
        }
        catch (Exception ex)
        {
            hasReadMachineId = false;
            hasReadMachineIdString = false;
            logger.LogError(ex, $"从 命令行参数 读取 {MachineIdKey}/{MachineIdStringKey}异常: {ex.Message}");
        }
        #endregion

        #region 再从 machine.txt 文件中获取
        if (!hasReadMachineId || !hasReadMachineIdString)
        {
            try
            {
                var baseDir = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory).Replace("\\", "/");
                var segs = baseDir.Split('/', StringSplitOptions.RemoveEmptyEntries);
                string filePath = null;
                for (int i = segs.Length; i > 0; i--)
                {
                    var tmpFile = Path.Combine(string.Join('/', segs.Take(i)), MachineTxtKey);
                    if (File.Exists(tmpFile))
                    {
                        filePath = tmpFile;
                        break;
                    }
                }
                if (filePath == null && baseDir.StartsWith('/'))
                {
                    //非windows系统 再增加根基目录下的查找
                    var tmpFile = Path.Combine("/", MachineTxtKey);
                    if (File.Exists(tmpFile))
                    {
                        filePath = tmpFile;
                    }
                }
                if (filePath != null)
                {
                    var lines = File.ReadAllLines(filePath);
                    if (!hasReadMachineId)
                    {
                        var machineIdline = lines.FirstOrDefault(line => line.Trim().StartsWith(MachineIdKey, StringComparison.OrdinalIgnoreCase));
                        if (int.TryParse(machineIdline.Split('=', StringSplitOptions.RemoveEmptyEntries)[1], out int tmpid))
                        {
                            if (tmpid >= 0)
                            {
                                machineId = tmpid;
                                hasReadMachineId = true;
                            }
                        }
                    }
                    if (!hasReadMachineIdString)
                    {
                        var machineIdStringline = lines.FirstOrDefault(line => line.Trim().StartsWith(MachineIdStringKey, StringComparison.OrdinalIgnoreCase));
                        machineIdString = machineIdStringline.Split('=', StringSplitOptions.RemoveEmptyEntries)[1].Trim();
                        hasReadMachineIdString = machineIdString.IsNotNullOrEmpty();
                    }
                }
            }
            catch (Exception ex)
            {
                hasReadMachineId = false;
                hasReadMachineIdString = false;
                logger.LogError(ex, $"从 machine.txt 读取 MachineId/MachineIdString 异常: {ex.Message}");
            }
        }
        #endregion

        #region 再从环境变量中获取
        if (!hasReadMachineId || !hasReadMachineIdString)
        {
            try
            {
                var dic = Environment.GetEnvironmentVariables();
                foreach (var item in dic)
                {
                    var ent = (DictionaryEntry)item;
                    var key = ent.Key.ToString();
                    if (!hasReadMachineId && string.Equals(key, MachineIdKey, StringComparison.OrdinalIgnoreCase))
                    {
                        if (int.TryParse(ent.Value?.ToString(), out int tmpid))
                        {
                            if (tmpid >= 0)
                            {
                                machineId = tmpid;
                                hasReadMachineId = true;
                                if (hasReadMachineId && hasReadMachineIdString) break;
                            }
                        }
                    }
                    if (!hasReadMachineIdString && string.Equals(key, MachineIdStringKey, StringComparison.OrdinalIgnoreCase))
                    {
                        machineIdString = ent.Value?.ToString()?.Trim();
                        hasReadMachineIdString = machineIdString.IsNotNullOrEmpty();
                        if (hasReadMachineId && hasReadMachineIdString) break;
                    }
                }
            }
            catch (Exception ex)
            {
                hasReadMachineId = false;
                hasReadMachineIdString = false;
                logger.LogError(ex, $"从 环境变量中 读取 {MachineIdKey}/{MachineIdStringKey} 异常: {ex.Message}");
            }
        }
        #endregion

        _machineId = machineId;
        _machineIdString = machineIdString;
    }
    #endregion

    private static bool? isAot = null;
    /// <summary>
    /// 是否是aot发布的项目
    /// </summary>
    public static bool IsRunningInAotMode()
    {
        if (isAot.HasValue) return isAot.Value;
        // 方法1：检查动态代码是否被支持
        if (!RuntimeFeature.IsDynamicCodeSupported)
        {
            isAot = true;
            return true;
        }

        // 方法2：检查AOT特定类型
        if (Type.GetType("System.Runtime.CompilerServices.Aot.Helper, System.Private.CoreLib") != null)
        {
            isAot = true;
            return true;
        }

        // 方法3：检查环境变量
        if (Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true")
        {
            isAot = true;
            return true;
        }
        isAot = false;
        return false;
    }
}
